/*
 * Copyright (c) 2017-2021, Lindenis Tech. Ltd.
 * All rights reserved.
 *
 * File:
 *
 * Description:
 *
 * Author:
 *      xiaoshujun@lindeni.com
 *
 * Create Date:
 *      2021/12/11
 *
 * History:
 *
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>

#include "osal_mutex.h"
#include "mididec.h"

#if ENABLE_AO
#include "adev_wrapper.h"
#endif

#include "osal_log.h"
#include "osal_common.h"

int g_ao_device = 0; // 0: AUDIO_DEVICE_CODEC, 1: AUDIO_DEVICE_HDMI
int g_ao_rate = 44100;
int g_ao_channel = 1;
int g_running = 1;

#if ENABLE_AO
_handle_t h_ao = 0;
#endif

_handle_t h_midi = 0;
mididec_config_t midi_cfg = {0};
lock_mutex g_lock;

#define SAVE_PCM 0
#if SAVE_PCM
FILE * g_fd = 0;
char * g_filepath = "/mnt/sdcard/1.pcm";
#endif

#if ENABLE_AO
static int _init_audio_out()
{
    int ret = 0;

    h_ao = adev_out_create();
    if (!h_ao) {
        loge("adev_out_create failed");
        return -1;
    }

    adev_config_t ao_cfg = {0};
    ao_cfg.channels = g_ao_channel;
    ao_cfg.rate = g_ao_rate;
    ao_cfg.bits = 16;
    ao_cfg.period_size = 576;
    ao_cfg.period_count = 2;
    ao_cfg.device = g_ao_device;
    ret = adev_out_open(h_ao, &ao_cfg);
    if (ret < 0) {
        loge("adev_out_open failed");
        goto _failed_;
    }
    return 0;

_failed_:
    adev_out_destroy(h_ao);
    return -1;
}
#endif


static void * _midi_decode_thread(void * arg)
{
    int ret = 0;
    logd("_midi_decode_thread running ...");

#if SAVE_PCM
    g_fd = fopen(g_filepath, "wb+");
    if (NULL == g_fd)
    {
        loge("Open %s file error", g_filepath);
        return 0;
    }
#endif

    h_midi = mididec_create();
    if(h_midi == 0) {
        loge("mididec_create failed");
        exit(1);
    }

    ret = mididec_start(h_midi, &midi_cfg);
    if (ret < 0) {
        loge("mididec_start failed");
        exit(1);
    }

    while(g_running)
    {
        av_frame2_t * p_frame = NULL;
        ret = mididec_receive_frame2(h_midi, &p_frame);
        if (ret == 0) {
#if ENABLE_AO
            //printf("got dec audio data pts: %lld, 0x%08x, size: %d\n", p_frame->pts, p_frame->data[0], p_frame->size[0]);
            adev_out_write(h_ao, p_frame->data[0], p_frame->size[0]);
#endif
#if SAVE_PCM
            if (g_fd)
            {
                fwrite(p_frame->data[0], p_frame->size[0], 1, g_fd);
            }
#endif
        }

        if (ret < 0) {
            g_running = 0;
            break;
        }
    }

    mididec_stop(h_midi);
    mididec_destroy(h_midi);

    logd("_midi_decode_thread exit");
#if SAVE_PCM
    if (NULL != g_fd)
    {
        fclose(g_fd);
        g_fd = NULL;
    }
#endif

    return 0;
}

static void print_usage(char * this)
{
    printf("\033[32m");
    printf("Usage: \n");
    printf("    %s [midi_file] <options>\n", this);
    printf("       [midi_file]  : the midi file path.\n");
    printf("options can be this:\n");
    printf("    -l [loop_count] : loop playback for aging test.\n");
    printf("    -d [device]     : audio output device, 0: codec, 1: hdmi, default: 0.\n");
    printf("    -c [channel]    : channels for audio output.\n");
    printf("    -r [rate]       : sample rate for audio output.\n");
    printf("    -v [volume]     : volume range: [0, 100].\n");
    printf("such as: %s /tmp/1.midi -d 0 -c 1 -r 16000 \n", this);
    printf("\033[0m\n");
}

int main(int argc, char **argv)
{
    int ret = 0, i = 0;
    int test_cnt = 0, cnt = 1;
    pthread_t tid_midi = 0;

    // default
    midi_cfg.midi_file = "/tmp/1.midi";
    midi_cfg.channels = g_ao_channel;
    midi_cfg.rate = g_ao_rate;
    midi_cfg.bits = 16;
    midi_cfg.volume = 100;

    if (argc == 1 || !strcmp(argv[1], "-h"))
    {
        print_usage(argv[0]);
        return 0;
    }
    else
    {
        midi_cfg.midi_file = argv[1];

        for (i = 2; i < argc; )
        {
            if (!strcmp(argv[i], "-l"))
            {
                test_cnt = atoi(argv[i+1]);
                i += 2;
            }
            else if (!strcmp(argv[i], "-d"))
            {
                g_ao_device = atoi(argv[i+1]);
                i += 2;
            }
            else if (!strcmp(argv[i], "-c"))
            {
                g_ao_channel = atoi(argv[i+1]);
                i += 2;
            }
            else if (!strcmp(argv[i], "-r"))
            {
                g_ao_rate = atoi(argv[i+1]);
                i += 2;
            }
            else if (!strcmp(argv[i], "-v"))
            {
                midi_cfg.volume = atoi(argv[i+1]);
                i += 2;
            }
            else
            {
                i++;
            }
        }
    }

    midi_cfg.channels = g_ao_channel;
    midi_cfg.rate = g_ao_rate;

    logi("play midi: %s, audio out: %s, channel: %d, rate: %d, bit: %d, volume: %d",
        midi_cfg.midi_file,
        g_ao_device ? "hdmi" : "codec",
        midi_cfg.channels,
        midi_cfg.rate,
        midi_cfg.bits,
        midi_cfg.volume);

    init_lock(&g_lock);

#if ENABLE_AO
    ret = _init_audio_out();
    if (ret < 0) {
        exit(1);
    }
#endif

_loop_:

    g_running = 1;
    ret = pthread_create(&tid_midi, NULL, _midi_decode_thread, NULL);
    if (ret)
    {
        loge("demux pthread_create() failed ");
        exit(1);
    }

    while(g_running)
    {
        signed char key_val;
        ret = wait_input_char(&key_val, 100000);
        if (ret < 0)
        {
            break;
        }
        else if (ret == 0)
        {
            continue;
        }

        if (key_val > 0 && key_val)
        {
            if (key_val != '\n')
            {
                printf("********* key_val: %c ********** \n", key_val);
            }

            if (key_val == 'q' || key_val == 'Q')
            {
                g_running = 0;
                cnt = test_cnt;
                break;
            }
        }
    }

    if (0 != tid_midi)
    {
        pthread_join(tid_midi, 0);
        tid_midi = 0;
    }

    if (cnt < test_cnt) {
        logd("\n\n\t test cnt: %d/%d \n\n", cnt++, test_cnt);
        goto _loop_;
    }

_loopexit_:

#if ENABLE_AO
    adev_out_close(h_ao);
    adev_out_destroy(h_ao);
#endif
    destroy_lock(&g_lock);

    logd("bye bye");

    return 0;
}
